<template>
  <view
    class="slider-range"
    :class="{ disabled: disabled }">
    <view class="slider-range-inner" :style="{ height: height + 'px' }">
      <view class="slider-bar" :style="{height: barHeight + 'px'}">
        <view class="slider-bar-bg" :style="{backgroundColor: backgroundColor}"></view>
        <view
          class="slider-bar-inner"
          :style="{
            width: (values / (max - min)) * 100 + '%',
            backgroundColor: activeColor}"></view>
        <view class="point-block" v-if="pointBlock">
          <view class="dots" v-for="(n, index) in (max + 1)" :key="n" :class="(higherHandlePosition * max / 100) > index ? 'active' :''"></view>
        </view>
      </view>
      <view
        class="slider-handle-block"
        :style="{
          width: '36px',
          height: '36px',
          left: higherHandlePosition + '%',
          zIndex: 1000
        }"
        @touchstart="_onTouchStart"
        @touchmove="_onBlockTouchMove"
        @touchend="_onBlockTouchEnd"
      >
      </view>
      <view
        class="slider-handle-block"
        :class="{ decoration: decorationVisible }"
        :style="{
          backgroundColor: blockColor,
          width: blockSize + 'px',
          height: blockSize + 'px',
          left: higherHandlePosition + '%',
        }" v-if="!blockImg"></view>
      <view
        class="slider-handle-block block-img"
        :style="{
          backgroundImage: `url(${blockImgUrl})`,
          left: higherHandlePosition + '%'
        }"
        :class="{ decoration: decorationVisible }" v-else></view>
      <view v-if="tipVisible" class="range-tip" :style="higherTipStyle">{{ format(values) }}</view>
    </view>
  </view>
</template>
<script setup>
import { ref, computed, watch, onMounted } from 'vue'
const props = defineProps({
  range: {
    type: Number,
    default: -1
  },
  min: {
    type: Number,
    default: 0
  },
  max: {
    type: Number,
    default: 100
  },
  step: {
    type: Number,
    default: 1
  },
  format: {
    type: Function,
    default: (val) => val
  },
  disabled: {
    type: Boolean,
    default: false
  },
  height: {
    height: Number,
    default: 40
  },
  barHeight: {
    type: Number,
    default: 5
  },
  backgroundColor: {
    type: String,
    default: '#e9e9e9'
  },
  activeColor: {
    type: String,
    default: '#1aad19'
  },
  blockSize: {
    type: Number,
    default: 20
  },
  blockColor: {
    type: String,
    default: '#fff'
  },
  tipVisible: {
    type: Boolean,
    default: true
  },
  decorationVisible: {
    type: Boolean,
    default: false
  },
  blockImg: {
    type: Boolean,
    default: false
  },
  blockImgUrl: {
    type: String,
    default: '#fff'
  },
  pointBlock: {
    type: Boolean,
    default: false
  }
})
const values = ref(0)
const startDragPos = ref(0)
const startVal = ref(0)
const isDragging = ref(false)
const higherHandlePosition = computed(() => {
  // 百分比不能超过100%
  const percentage = values.value / props.max
  if (percentage >= 1) {
    return 100
  } else {
    return percentage < 0 ? 0 : percentage * 100
  }
})
const higherTipStyle = computed(() => {
  if (props.higherHandlePosition < 90) {
    return `left: ${props.higherHandlePosition}%;`
  }
  return `right: ${100 - props.higherHandlePosition}%;transform: translate(50%, -100%);`
})
const emit = defineEmits(['change', 'dragOver', 'dragIng', 'dragMove'])
// 初始化
onMounted(() => {
  if ((props.range !== values.value) && !isDragging.value) {
    _updateValue && _updateValue(props.range)
  }
})
watch(() => props.range, (newVal, oldVal) => {
  if ((newVal !== values.value) && !isDragging.value) {
    _updateValue && _updateValue(newVal)
  }
}, { immediate: true })
const _updateValue = (newVal) => {
  if (props.step >= props.max) {
    throw new RangeError('Invalid slider step or slider range')
  }
  let newValues = newVal
  newValues = Math.round((newValues) / props.step) * props.step
  // 新值与原值相等，不做处理
  if (values.value === newValues && props.range === newValues) {
    return
  }
  // 不能超过最大值
  if (newValues >= props.max) {
    newValues = props.max
  }
  values.value = newValues
  emit('change', values.value)
}
const _onTouchStart = (event) => {
  if (props.disabled) {
    return
  }
  isDragging.value = true
  const e = event.changedTouches ? event.changedTouches[0] : event
  startDragPos.value = e.pageX
  startVal.value = values.value
  emit('dragIng')
}
const _onBlockTouchMove = (e) => {
  if (props.disabled) {
    return
  }
  _onDrag(e)
  emit('dragMove', values.value)
}
const _onBlockTouchEnd = (e) => {
  if (props.disabled) {
    return
  }
  isDragging.value = false
  _onDrag(e)
  emit('dragOver', values.value)
}
const _onDrag = (event) => {
  if (!isDragging.value) {
    return
  }
  uni.getSystemInfo({
    success: (res) => {
      const sliderWidth = res.screenWidth - 26
      const e = event.changedTouches ? event.changedTouches[0] : event
      const diff = ((e.pageX - startDragPos.value) / sliderWidth) * props.max
      const nextVal = startVal.value + diff
      _updateValue(nextVal)
    }
  })
}
</script>

<style scoped>
.slider-range {
  position: relative;
  padding: 0 13px;
}
.slider-range-inner {
  position: relative;
  width: 100%;
}
.slider-range.disabled .slider-bar-inner {
  opacity: 0.35;
}
.slider-range.disabled .slider-handle-block {
  cursor: not-allowed;
}
.slider-bar {
  position: absolute;
  top: 50%;
  left: 0;
  right: 0;
  transform: translateY(-50%);
}
.point-block{
  display: flex;
  justify-content: space-between;
  height: 20rpx;
  position: relative;
  z-index: 14;
}
.point-block .dots{
  width: 20rpx;
  height: 20rpx;
  border-radius: 50%;
  background-color: #CACACA;
}
.point-block .dots.active{
  background-color: #F39A41
}
.slider-bar-bg {
  position: absolute;
  width: 100%;
  height: 100%;
  border-radius: 10000px;
  z-index: 10;
}
.slider-bar-inner {
  position: absolute;
  width: 100%;
  height: 100%;
  border-radius: 10000px;
  z-index: 11;
}
.slider-handle-block {
  position: absolute;
  top: 50%;
  transform: translate(-50%, -50%);
  border-radius: 50%;
  z-index: 12;
}
.block-img{
  width: 47rpx;
  height: 35rpx;
  background-size: 100%;
  border-radius: 0;
}
.range-tip {
  position: absolute;
  top: 0;
  font-size: 24upx;
  color: #666;
  transform: translate(-50%, -100%);
}
</style>
